﻿//#define ATFS_HASH_DISPLAY
//#define PARTICIPANTS
using System;
using System.Diagnostics;
using System.Text;
using System.IO;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading;

using alib.Enumerable;
using alib.Debugging;
using alib.Tally;
using alib.Collections;
using alib.BitArray;
using alib.Concurrency;
using alib;
using alib.String;

/* ALERT ALERT ALERT. THE REASON A DebuggerTypeProxy MIGHT NOT WORK IS BECAUSE 
 * THE DISPLAY MEMBERS ARE NOT PUBLIC. RATHER THAN USE DebuggerBrowsableAttribute,
 * YOU SHOULD JUST USE public vs. private
 * */


namespace agree
{
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// To abort processing at any point and send up a TFS for examination
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class DebuggingTfsDisplayException : Exception
	{
		Tfs[] rgtfs;

		public DebuggingTfsDisplayException(Tfs tfs)
			: base(String.Format("Debugging exception for TFS {0}", tfs.ToString()))
		{
			this.rgtfs = new Tfs[] { tfs };
		}

		public DebuggingTfsDisplayException(params Tfs[] rgtfs)
		{
			this.rgtfs = rgtfs;
		}

		public Tfs[] Tfs { get { return rgtfs; } }
	}

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/// <summary>
	/// To abort processing at any point and send up a TFS for examination
	/// </summary>
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public class DebuggingTfsDisplayExceptionWithHighlighting : DebuggingTfsDisplayException
	{
		Tfs tfsHighlight;

		public DebuggingTfsDisplayExceptionWithHighlighting(Tfs tfs, Tfs highlight)
			: base(tfs)
		{
			this.tfsHighlight = highlight;
		}

		public Tfs TfsHighlight { get { return tfsHighlight; } }
	}


	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	[DebuggerDisplay("{ToString(),nq}")]
	public partial struct Edge
	{
#if DEBUG
		public String ToString(FeatMgr ftm, bool f_grammar_certain)
		{
			return FormatMark(this) + " " + TypeInfoFromFlag(ftm, FlagsId, false);
		}

		public override String ToString()
		{
			return ToString(Grammar.DebugGrammar.ftm, false);
		}

#endif
		public static class Chars
		{
			static Chars()
			{
				bool f = _dbg_util.IsGuiApplication || Debugger.IsAttached;
				if (f)
				{
					Chars.Top = "⊤";
					Chars.Bottom = "⊥";
					Chars.Coref = "⇌";
					Chars.Atomic = "⊣";
					Chars.Complex = "…";
					Chars.NonBare = "☒";
					Chars.Bare = "☐";
				}
				else
				{
					Chars.Top = "T";
					Chars.Bottom = "_";
					Chars.Coref = "%";
					Chars.Atomic = ".";
					Chars.Complex = "...";
					Chars.NonBare = "X";
					Chars.Bare = "_";
				}
			}
			public static String Top, Bottom, Coref, Atomic, Complex, NonBare, Bare;
		}

		public static String TypeInfoFromFlag(FeatMgr ftm, Flag f_in, bool f_coref = false)
		{
			if (f_in == Flag.Bottom)
				return Chars.Bottom;

			String s = "";
			if (f_coref && f_in < 0)
				s += Edge.Chars.Coref + " ";

			int id = (int)(f_in & Flag.MultiIdMask);
			if (f_in.IsStringFamilyType())
			{
				if (f_in.IsSkolemValue())
					s += TypeUtils.GetSkolemString(f_in);
				else if (f_in.IsIntegerValue())
					s += "(" + id.ToString() + ")";
				else
				{
					if (ftm == null)
						s += "string (n/a)";
					else if (f_in.IsTheStringType())
						s += ftm.AgreeConfig.Types.StringType;
					else
						s += ftm.td.strings[id].DoubleQuote();
					s += " " + Chars.Atomic;
				}
			}
			else if (id == 0)
				s += Edge.Chars.Top;
			else if (ftm == null)
				s += "(TypeMgr not available)";
			else if (id >= ftm.bcpo.type_arr.Length)
				return String.Format("bad type id: {0:X8}: out of range for type_arr.", id);
			else
				s += ftm.bcpo.type_arr[id].Name + ((f_in & Flag.EtmNonBareType) != 0 ? Edge.Chars.Complex : " ⊣");

			return s;
		}

		public static String FormatMark(Edge e)
		{
			if (e.IsCoreferenced)
				return Edge.Chars.Coref + e.Mark.ToString();
			if (e.Mark != 0)
				return "@" + e.Mark.ToString();
			if ((e.FlagsId & Flag.EtmNonBareType) != 0)
				return Edge.Chars.NonBare;
			return Edge.Chars.Bare;
		}
	};

#if DEBUG

	[DebuggerDisplay("{ToString(),nq}")]
	[DebuggerTypeProxy(typeof(arraytfs_debug_display))]
	public unsafe partial class _atfs_base : Tfs
	{
	};

	[DebuggerDisplay("{ToString(),nq}")]
	[DebuggerTypeProxy(typeof(arraytfs_debug_display))]
	public abstract unsafe partial class TfsBuilder : TdlParsePos
	{
		[DebuggerDisplay("build from TDL: '{this.ShortDisplayInfo(),nq}' cur:'{CurrentSafe.ToString(),nq}'  {EdgeCount} edges.  next_mark: {next_mark}")]
		[DebuggerTypeProxy(typeof(arraytfs_debug_display))]
		public partial class Tdl : TfsBuilder
		{
		};
	};


	public class arraytfs_debug_display
	{
		public arraytfs_debug_display(Tfs atfs)
		{
			this.atfs = atfs;
		}
		Tfs atfs;

		[DebuggerDisplay("Length={atfs.c_act}", Name = "Entries (natural order)")]
		public TfsSlotRef[] z1_natural_order
		{
			get
			{
				TfsSlotRef[] arr = new TfsSlotRef[atfs.c_act];
				for (int i = 0; i < atfs.c_act; i++)
					arr[i] = new TfsSlotRef(atfs, i + 1);
				return arr;
			}
		}

		[DebuggerDisplay("", Name = "Entries (in mark)")]
		public TfsSlotRef[] z2_by_in_mark
		{
			get
			{
				TfsSlotRef[] arr = new TfsSlotRef[atfs.c_act];
				for (int i = 0; i < atfs.c_act; i++)
					arr[i] = new TfsSlotRef(atfs, i + 1);
				Array.Sort(arr, (a, b) =>
				{
					var _a = a.tfs.entries[a.ix0];
					var _b = b.tfs.entries[b.ix0];
					int d = _a.mark - _b.mark;
					if (d != 0)
						return d;
					return _a.i_feat - _b.i_feat;
				});
				return arr;
			}
		}

		[DebuggerDisplay("", Name = "Entries (out mark)")]
		public TfsSlotRef[] z3_by_out_mark
		{
			get
			{
				TfsSlotRef[] arr = new TfsSlotRef[atfs.c_act];
				for (int i = 0; i < atfs.c_act; i++)
					arr[i] = new TfsSlotRef(atfs, i + 1);
				Array.Sort(arr, (a, b) =>
				{
					var _a = a.tfs.entries[a.ix0];
					var _b = b.tfs.entries[b.ix0];
					return _a.e_Mark - _b.e_Mark;
				});
				return arr;
			}
		}

		[DebuggerDisplay("", Name = "Hash display")]
		public unsafe arraytfs_hash_display[] z4_hash_display
		{
			get
			{
				var _a = atfs as ArrayTfs;
				if (_a == null)
					return Collection<arraytfs_hash_display>.None;

				arraytfs_hash_display[] arr;
				fixed (ushort* phf = _a.h.pfirst)
				{
					int c = 0;
					for (int i = 0; i < 256; i++)
						if (phf[i] != 0)
							c++;

					arr_tfs_entry[] rgate = _a.entries;
					arr = new arraytfs_hash_display[c];
					for (int i = 0, j = 0; i < 256; i++)
					{
						ushort ix1_first = phf[i];
						if (ix1_first != 0)
							arr[j++] = new arraytfs_hash_display { tfs = _a, ix1_first = ix1_first };
					}
					Array.Sort(arr, (a, b) => a._hv - b._hv);
				}
				return arr;
			}
		}

		[DebuggerDisplay("{z5_flags.ToString(),nq}", Name = "TFS flags")]
		public Tfs.Flags z5_flags { get { return atfs.flags; } }
		[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		TfsSlot[] _BELOW { get { return SlotRefArray.GetConstraints(atfs); } }
	};

	[DebuggerDisplay("{ToString(),nq}", Name = "#{_hv.ToString().PadRight(3),nq}  {ix1_first.ToString().PadLeft(3) + (\"(\"+(ix1_first-1).ToString()+\")\").PadRight(3),nq}")]
	public class arraytfs_hash_display
	{
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public Tfs tfs;
		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int ix1_first;

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int Count
		{
			get
			{
				arr_tfs_entry[] rgate = tfs.entries;
				int c = 0;
				int ix = ix1_first;
				while (ix != 0)
				{
					ix = rgate[ix - 1].next;
					c++;
				}
				return c;
			}
		}

		[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		public TfsSlotRef[] _list
		{
			get
			{
				arr_tfs_entry[] rgate = tfs.entries;

				int c = 0;
				int ix = ix1_first;
				while (ix != 0)
				{
					ix = rgate[ix - 1].next;
					c++;
				}

				var arr = new TfsSlotRef[c];
				ix = ix1_first;
				while (ix != 0)
				{
					ix--;
					arr[--c] = new TfsSlotRef(tfs, ix + 1);
					ix = rgate[ix].next;
				}
				//Array.Sort(arr, (a, b) => a.ix - b.ix);
				return arr;
			}
		}

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public arr_tfs_entry _ent { get { return tfs.entries[ix1_first - 1]; } }

		[DebuggerBrowsable(DebuggerBrowsableState.Never)]
		public int _hv
		{
			get
			{
				var e = _ent;
				return (byte)(e.i_feat ^ e.mark);
			}
		}

		public override string ToString()
		{
			var l = _list;
			return String.Format("#{0,-3}(0x{0:X2}) c:{1,2}  {2}",
				_hv,
				l.Length,
				l.Select(ated => String.Format("{0}(ix:{1})", ated.ix0 + 1, ated.ix0)).StringJoin(" -> "));
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	public abstract partial class LatticeType : Instance
	{
		[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		Type[] _dbg_parents { get { return parents.ToArray(); } }

		Type GreatestLowerBound(Type other)
		{
			if (this.IsLeaf && other.IsLeaf)
				return null;
			HashSet<Type> cln = CommonLowerNodes(other);
			if (cln.Count == 0)
				return null;
			if (cln.Count == 1)
				return cln.First();
			if (cln.Contains(this))
				return this as Type;
			if (cln.Contains(other))
				return other;
			throw new Exception();
		}

		public HashSet<Type> CommonLowerNodes(Type other)
		{
			HashSet<Type> h = AllDescendantsInclusive;
			h.IntersectWith(other.AllDescendantsInclusive);
			return h;
		}

		/// only returns error cases
		public HashSet<Type> GlbCheck(Type other, Type root)
		{
			if (this as Type == other)
				return null;
			if (this.IsLeaf || other.IsLeaf)	// one might be GLB, but there surely won't be a problem
				return null;
			if (this as Type == root || other == root)
				return null;

			HashSet<Type> cln = CommonLowerNodes(other);
			if (cln.Count <= 1 || cln.Contains(this as Type) || cln.Contains(other))
				return null;
			// get rid of deeper extras
			HashSet<Type> h = new HashSet<Type>();
			foreach (Type t in cln)
				h.UnionWith(t.AllDescendants);
			cln.RemoveWhere(e => h.Contains(e));
			if (cln.Count == 0)
				throw new Exception();
			// that might have done the trick
			if (cln.Count == 1)
				return null;
			return cln;
		}

		public String _dbg_inf
		{
			get
			{
				bool f_ones = bit_vec != null;

				String child_inf;
				if (f_ones && (c_children == 0) != (bit_vec.OnesCount == 1))
					child_inf = "error";
				else if (c_children == 0)
					child_inf = "leaf";
				else
					child_inf = String.Format("{0}>{1}", c_children, !f_ones ? "n/a" : 0.ToString());

				String cc_inf = String.Format("{0,2}<{1}|{2}", 0, parents.Count, child_inf);

				return String.Format("{0,4} {1,4} {2,7} {3,-12} {4,-30} {5}",
					bit_num == -1 ? "top" : bit_num == -2 ? "GLB" : bit_num.ToString(),
					bit_vec == null ? "null" : !f_ones ? "n/a" : bit_vec.OnesCount.ToString(),
					"pv:?",
					IsLeaf ? "(leaf)" : cc_inf,
					Name ?? "(no name)",
					bit_vec == null ? "(no code)" : !f_ones ? "n/a" : bit_vec.OnesPositions().StringJoin(" "));
			}
		}
	};

	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	///
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	//[DebuggerDisplay("{Name,nq} {m_id} ({_feature_info(),nq})")]
	public sealed partial class Type : LatticeType
	{
#if false
		//[DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
		[DebuggerDisplay("{_feature_info(),nq}", Name = "feature info")]
		public FeatureInfo[] _dbg_features
		{
			get
			{
				if (fc == null)
					return Collection<FeatureInfo>.None;
				//FCM fcm = (FCM)tm;
				return fc.rg_fix.Select(i_feat => ftm[i_feat]).ToArray();
			}
		}
#endif

		String _feature_info()
		{
			if (fc == null)
				return Name;
			int local = 0, total = 0;
			foreach (FeatureInfo fi in fc.rg_fix.Select(i_feat => ftm[i_feat]))
			{
				if (fi.introduced_by == this)
					local++;
				total++;
			}
			return String.Format("tot:{0}  [ {1} ]  loc:{2}  inh:{3}",
				total,
				fc.rg_fix.Select(i => i == -1 ? "" : ftm[i].feature.ToUpper()).StringJoin(" "),
				local,
				total - local);
		}
	};
#endif // DEBUG
}
